#pragma once
#include "Socket.hpp"
#include <sys/select.h>
using namespace socket_ns;

class SelectServer
{
    const static int N = sizeof(fd_set) * 8;
    const static int DefaultFd = -1;

public:
    SelectServer(uint16_t port)
        : _port(port), _listenSock(std::make_unique<TcpSocket>())
    {
        _listenSock->BuildListenServer(INetAddr("0", port));
        for (int i = 0; i < N; i++)
            _fdArray[i] = DefaultFd;
        // 设置listen socketfd
        _fdArray[0] = _listenSock->Socketfd();
    }
    void AcceptClient()
    {
        INetAddr clientAddr;
        int fd = _listenSock->Acceptor(clientAddr);
        if (fd < 0)
        {
            LOG(WARNING, "accept fail\n");
            return;
        }
        LOG(DEBUG, "get a link, fd : %d, client : [%s:%d]\n", fd, clientAddr.IP().c_str(), clientAddr.Port());

        int pos = 1;
        for (; pos < N; pos++)
        {
            if (_fdArray[pos] == DefaultFd)
                break;
        }
        if (pos == N)
        {
            LOG(WARNING, "server is full\n");
            ::close(fd);
            return;
        }
        else
        {
            _fdArray[pos] = fd;
            LOG(DEBUG, "%d is add to select array\n", fd);
        }
        LOG(INFO, "fd array[] : %s\n", RfdsToString().c_str());
    }
    void ServiceIO(int pos)
    {
        char buff[1024];
        ssize_t n = ::recv(_fdArray[pos], buff, sizeof(buff) - 1, 0);
        if (n > 0)
        {
            buff[n] = '\0';
            std::cout << "client say# " << buff << std::endl;
            std::string result = std::string("[server say]# ") + buff;
            ::send(_fdArray[pos], result.c_str(), result.size(), 0);
        }
        // 客户端退出
        else if (n == 0)
        {
            ::close(_fdArray[pos]);
            _fdArray[pos] = DefaultFd;
            LOG(INFO, "fd array[] : %s\n", RfdsToString().c_str());
        }
        else
        {
            ::close(_fdArray[pos]);
            _fdArray[pos] = DefaultFd;
            LOG(INFO, "fd array[] : %s\n", RfdsToString().c_str());
        }
    }
    void Handler(fd_set &rfds)
    {
        for (int i = 0; i < N; i++)
        {
            if (_fdArray[i] == DefaultFd)
                continue;
            // 走到当前位置，一定是一个有效的fd
            // 如果当前位图中的当前fd已经就绪
            if (FD_ISSET(_fdArray[i], &rfds))
            {
                // listen socket
                if (_fdArray[i] == _listenSock->Socketfd())
                {
                    AcceptClient();
                }
                // normal socket
                else
                {
                    ServiceIO(i);
                }
            }
        }
    }
    void Loop()
    {
        while (true)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxFd = DefaultFd;
            for (int i = 0; i < N; i++)
            {
                if (_fdArray[i] == DefaultFd)
                    continue;
                FD_SET(_fdArray[i], &rfds);
                if(_fdArray[i] > maxFd)
                    maxFd = _fdArray[i];
            }

            struct timeval timeout = {1, 0};
            int n = ::select(maxFd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr);
            switch (n)
            {
            case 0:
                LOG(INFO, "time out, %d.%d\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                LOG(FATAL, "select error\n");
                break;
            default:
                LOG(DEBUG, "event happen!\n");
                Handler(rfds);
                break;
            }
        }
    }
    std::string RfdsToString()
    {
        std::string s;
        for (int i = 0; i < N; i++)
        {
            if (_fdArray[i] != DefaultFd)
            {
                s += std::to_string(_fdArray[i]) + ' ';
            }
        }
        return s;
    }
    ~SelectServer()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenSock;
    int _fdArray[N];
};